#include "StdAfx.h"
#include "SpotLightSphere.h"
#include "Level.h"
#include "GraphCamera.h"

#include "math.h"

SpotLightSphere::SpotLightSphere(const tstring &texfilename, float m_Radius, UINT m_numSlices, UINT m_NumStacks, float UScale, float VScale) 
	: LevelElement()
	,m_pVertexLayout(0)
	,m_pVertexBuffer(0)
	,m_pIndexBuffer(0)
	,m_pTextureRV(0)	
	,m_pDefaultEffect(0)	
	,m_pDefaultTechnique(0)	
	,m_pWorldViewProjectionVariable(NULL)
	,m_pWorldVariable(0)
	,m_pDiffuseMapVariabele(0)
	,m_VertexBufferStride(0)
	,m_TextureName(texfilename)
	,m_NumIndices(0)
	,m_Radius(m_Radius)
	,m_NumSlices(m_numSlices)
	,m_NumStacks(m_NumStacks)
	,m_UScale(UScale)
	,m_VScale(VScale)
{
}

SpotLightSphere::~SpotLightSphere()
{
	SafeRelease(m_pVertexBuffer );
	SafeRelease(m_pVertexLayout);
	SafeRelease(m_pIndexBuffer);
}

void SpotLightSphere::Initialize(ContentManager *pContentManager)
{
	CreateEffect(pContentManager);
	m_pTextureRV = pContentManager->GetTexture(m_pLevel->GetDevice(), m_TextureName);
	DefineInputlayout();
	FillVertexAndIndexBuffer();
}

void SpotLightSphere::FillVertexAndIndexBuffer()
{
	vector<VertexPosNormTex> vertices;
	vector<DWORD> indices;
	BuildStacks(vertices, indices);
	
	D3D10_BUFFER_DESC vbd;
    vbd.Usage = D3D10_USAGE_IMMUTABLE;
    vbd.ByteWidth = sizeof(VertexPosNormTex) * (UINT)vertices.size();
    vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    vbd.CPUAccessFlags = 0;
    vbd.MiscFlags = 0;
	D3D10_SUBRESOURCE_DATA vinitData;
    vinitData.pSysMem = &vertices[0];
    HR(m_pLevel->GetDevice()->CreateBuffer(&vbd, &vinitData, &m_pVertexBuffer));

	D3D10_BUFFER_DESC ibd;
    ibd.Usage = D3D10_USAGE_IMMUTABLE;
    ibd.ByteWidth = sizeof(DWORD) * indices.size();
    ibd.BindFlags = D3D10_BIND_INDEX_BUFFER;
    ibd.CPUAccessFlags = 0;
    ibd.MiscFlags = 0;
    D3D10_SUBRESOURCE_DATA initData;
    initData.pSysMem = &indices[0];
    HR(m_pLevel->GetDevice()->CreateBuffer(&ibd, &initData, &m_pIndexBuffer));

	m_NumIndices = (UINT)indices.size();
}
void SpotLightSphere::Draw(const RenderContext* pRenderContext) 
{
	if(!m_pDefaultTechnique )
	{
		MessageBox(0,_T("No Technique"),_T("ERROR"),0);
		return;
	}
	if(!m_pVertexBuffer || !m_pIndexBuffer)
	{
		MessageBox(0,_T("No Vertices"),_T("ERROR"),0);
		return;
	}

	 // Update Shader matrix variables
	D3DXMATRIX matView = pRenderContext->GetCamera()->GetView();
	D3DXMATRIX matProj = pRenderContext->GetCamera()->GetProj();
	m_pWorldViewProjectionVariable->SetMatrix((float*)&(m_World*matView*matProj));
	m_pWorldVariable->SetMatrix((float*)&m_World);
	//set the texture variabele
	m_pDiffuseMapVariabele->SetResource(m_pTextureRV);	

	// Set the input layout
    m_pLevel->GetDevice()->IASetInputLayout( m_pVertexLayout );

    // Set vertex buffer
    UINT offset = 0;
    m_pLevel->GetDevice()->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &m_VertexBufferStride, &offset );
	// Set index buffer
	m_pLevel->GetDevice()->IASetIndexBuffer(m_pIndexBuffer,DXGI_FORMAT_R32_UINT,0);
    // Set primitive topology
    m_pLevel->GetDevice()->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

    //
    // Renders a ShapeBase
    //
    D3D10_TECHNIQUE_DESC techDesc;
    m_pDefaultTechnique->GetDesc( &techDesc );
    for( UINT p = 0; p < techDesc.Passes; ++p )
    {
        m_pDefaultTechnique->GetPassByIndex(p)->Apply(0);
		m_pLevel->GetDevice()->DrawIndexed( m_NumIndices, 0, 0 ); 
    }
}
void SpotLightSphere::BuildStacks(vector<VertexPosNormTex>& vertices, vector<DWORD>& indices)
{
	float phiStep = (float)D3DX_PI/m_NumStacks;

	// do not count the poles as rings
	UINT numRings = m_NumStacks-1;

	// Compute vertices for each stack ring.
	for(UINT i = 1; i <= numRings; ++i)
	{
		float phi = i*phiStep;

		// vertices of ring
		float thetaStep = 2.0f * (float)D3DX_PI / m_NumSlices;
		for(UINT j = 0; j <= m_NumSlices; ++j)
		{
			float theta = j*thetaStep;

			VertexPosNormTex v;

			// spherical to cartesian
			v.pos.x = m_Radius*sinf(phi)*cosf(theta);
			v.pos.y = m_Radius*cosf(phi);
			v.pos.z = m_Radius*sinf(phi)*sinf(theta);

			// partial derivative of P with respect to theta
			//v.tangent.x = -m_Radius*sinf(phi)*sinf(theta);
			//v.tangent.y = 0.0f;
			//v.tangent.z = m_Radius*sinf(phi)*cosf(theta);

			D3DXVec3Normalize(&v.normal, &v.pos);

			v.tex.x = theta / (2.0f*(float)D3DX_PI)*m_UScale;
			v.tex.y = phi / (float)D3DX_PI * m_VScale;

			vertices.push_back( v );
		}
	}

	// poles: note that there will be texture coordinate distortion
	vertices.push_back( VertexPosNormTex(0.0f, -m_Radius, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f) );
	vertices.push_back( VertexPosNormTex(0.0f, m_Radius, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f) );

	UINT northPoleIndex = (UINT)vertices.size()-1;
	UINT southPoleIndex = (UINT)vertices.size()-2;

	UINT numRingVertices = m_NumSlices+1;

	// Compute indices for inner stacks (not connected to poles).
	for(UINT i = 0; i < m_NumStacks-2; ++i)
	{
		for(UINT j = 0; j < m_NumSlices; ++j)
		{
			indices.push_back(i*numRingVertices + j);
			indices.push_back(i*numRingVertices + j+1);
			indices.push_back((i+1)*numRingVertices + j);

			indices.push_back((i+1)*numRingVertices + j);
			indices.push_back(i*numRingVertices + j+1);
			indices.push_back((i+1)*numRingVertices + j+1);
		}
	}

	// Compute indices for top stack.  The top stack was written 
	// first to the vertex buffer.
	for(UINT i = 0; i < m_NumSlices; ++i)
	{
		indices.push_back(northPoleIndex);
		indices.push_back(i+1);
		indices.push_back(i);
	}

	// Compute indices for bottom stack.  The bottom stack was written
	// last to the vertex buffer, so we need to offset to the index
	// of first vertex in the last ring.
	UINT baseIndex = (numRings-1)*numRingVertices;
	for(UINT i = 0; i < m_NumSlices; ++i)
	{
		indices.push_back(southPoleIndex);
		indices.push_back(baseIndex+i);
		indices.push_back(baseIndex+i+1);
	}
}
void SpotLightSphere::DefineInputlayout()
{
    // Define the input layout
    D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },  
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 }, 
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 }
    };
    UINT numElements = sizeof(layout)/sizeof(layout[0]);

    // Create the input layout
    D3D10_PASS_DESC PassDesc;
	// Get the pass decriptor from the effect technique
    HR(m_pDefaultTechnique->GetPassByIndex( 0 )->GetDesc( &PassDesc ));
    HR(m_pLevel->GetDevice()->CreateInputLayout( layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &m_pVertexLayout ));

	//important set the size of the vertexbuffer, is used in draw base member function
	m_VertexBufferStride = sizeof(VertexPosNormTex);
}
void SpotLightSphere::CreateEffect(ContentManager *pContentManager)
{
	m_pDefaultEffect = pContentManager->GetEffect(m_pLevel->GetDevice(), _T("./Effect/PNTa.fx"));	

	//get first technique found
	m_pDefaultTechnique = m_pDefaultEffect->GetTechniqueByIndex(0);

	GetEffectVariables(m_pDefaultEffect);

}
void SpotLightSphere::SetEffect(ID3D10Effect* effect)
{
	m_pDefaultEffect = effect;

	//get first technique available
	m_pDefaultTechnique = m_pDefaultEffect->GetTechniqueByIndex(0);

	GetEffectVariables(m_pDefaultEffect);

}
void SpotLightSphere::GetEffectVariables(ID3D10Effect* pEffect)
{
	//get effect variables
	m_pWorldViewProjectionVariable = pEffect->GetVariableByName("gWVP")->AsMatrix();
	if(!m_pWorldViewProjectionVariable->IsValid())MessageBox(0,_T("Getting EffectSemantic WorldViewProjection Failed"),_T("ERROR"),0);

	m_pWorldVariable = pEffect->GetVariableByName("gWorld")->AsMatrix();
	if(!m_pWorldVariable->IsValid())MessageBox(0,_T("Getting EffectSemantic World Failed"),_T("ERROR"),0);
	
	m_pDiffuseMapVariabele = pEffect->GetVariableByName( "gDiffuseMap" )->AsShaderResource();
	if(!m_pDiffuseMapVariabele->IsValid())MessageBox(0,_T("Getting EffectSemantic DiffuseMap Failed"),_T("ERROR"),0);

}